//go:build homework

package homework

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

var errInvalidEntity = errors.New("invalid entity")

func InsertStmt(entity interface{}) (string, []interface{}, error) {
	if entity == nil {
		return "", nil, errInvalidEntity
	}
	rt := reflectType(entity)
	if rt.Kind() == reflect.Ptr {
		return "", nil, errInvalidEntity
	}
	rv := reflectValue(entity)
	fieldCount := rv.NumField()
	if fieldCount == 0 {
		return "", nil, errInvalidEntity
	}
	bd := strings.Builder{}
	bd.WriteString("INSERT INTO ")
	bd.WriteString(fmt.Sprintf("`%v`", rt.Name()))
	fieldMap := make(map[string]bool)
	firstLevel := true
	fields, values, args := parseStruct(rt, rv, fieldMap, firstLevel)
	bd.WriteString(fmt.Sprintf("(%v)", strings.Join(fields, ",")))
	bd.WriteString(fmt.Sprintf(" VALUES(%v);", strings.Join(values, ",")))
	return bd.String(), args, nil

	// 检测 entity 是否符合我们的要求
	// 我们只支持有限的几种输入

	// 使用 strings.builder 来拼接 字符串
	// bd := strings.builder{}

	// 构造 INSERT INTO XXX，XXX 是你的表名，这里我们直接用结构体名字

	// 遍历所有的字段，构造出来的是 INSERT INTO XXX(col1, col2, col3)
	// 在这个遍历的过程中，你就可以把参数构造出来
	// 如果你打算支持组合，那么这里你要深入解析每一个组合的结构体
	// 并且层层深入进去

	// 拼接 VALUES，达成 INSERT INTO XXX(col1, col2, col3) VALUES

	// 再一次遍历所有的字段，要拼接成 INSERT INTO XXX(col1, col2, col3) VALUES(?,?,?)
	// 注意，在第一次遍历的时候我们就已经拿到了参数的值，所以这里就是简单拼接 ?,?,?
}

func reflectType(entity interface{}) reflect.Type {
	if t := reflect.TypeOf(entity); t.Kind() == reflect.Ptr {
		return t.Elem()
	} else {
		return t
	}
}

func reflectValue(entity interface{}) reflect.Value {
	if t := reflect.ValueOf(entity); t.Kind() == reflect.Ptr {
		return t.Elem()
	} else {
		return t
	}
}

func parseStruct(rt reflect.Type, rv reflect.Value, fieldMap map[string]bool, firstLevel bool) ([]string, []string, []interface{}) {
	fieldCount := rv.NumField()
	fields := make([]string, 0)
	values := make([]string, 0)
	args := make([]interface{}, 0)
	for i := 0; i < fieldCount; i++ {
		switch rv.Field(i).Kind() {
		case reflect.Struct:
			// if !rt.Field(i).Anonymous || rt.Field(i).Type.String() == "sql.NullString" {
			if !rt.Field(i).Anonymous {
				if fieldMap[rt.Field(i).Name] {
					continue
				}
				fieldMap[rt.Field(i).Name] = true
				fields = append(fields, fmt.Sprintf("`%v`", rt.Field(i).Name))
				values = append(values, "?")
				args = append(args, rv.Field(i).Interface())
			} else {
				fieldsInside, valuesInside, argsInside := parseStruct(rt.Field(i).Type, rv.Field(i), fieldMap, false)
				fields = append(fields, fieldsInside...)
				values = append(values, valuesInside...)
				args = append(args, argsInside...)
			}
		default:
			if fieldMap[rt.Field(i).Name] {
				continue
			}
			fieldMap[rt.Field(i).Name] = true
			fields = append(fields, fmt.Sprintf("`%v`", rt.Field(i).Name))
			values = append(values, "?")
			args = append(args, rv.Field(i).Interface())
		}
	}
	return fields, values, args
}
